package cn.wbnull.springbootdemo.boot;

import cn.wbnull.springbootdemo.enity.Direction;
import cn.wbnull.springbootdemo.enity.GameRoom;
import cn.wbnull.springbootdemo.enity.Player;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/*
 * @Author:大力王
 * @Date:2025/6/5-05-12:43
 * 开始了
 */
@Component
public class SnakeWebSocketHandler extends TextWebSocketHandler {

    private static final ObjectMapper mapper = new ObjectMapper();
    private static final Map<String, GameRoom> rooms = new ConcurrentHashMap<>();
    private static final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>();
    private static final ConcurrentHashMap<String, Object> sessionLocks = new ConcurrentHashMap<>();

    // 修改消息发送线程池为固定大小
    private static final ExecutorService messageExecutor = Executors.newFixedThreadPool(4);

    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        sessions.put(session.getId(), session);
        sessionLocks.put(session.getId(), new Object());
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) {
        // 使用锁来保证线程安全
        Object sessionLock = sessionLocks.computeIfAbsent(session.getId(), k -> new Object());

        try {
            synchronized (sessionLock) {  // 使用专用锁对象
                Map<String, Object> payload = mapper.readValue(message.getPayload(),
                        new TypeReference<>() {
                        });
                String type = (String) payload.get("type");

                switch (type) {
                    case "joinRoom":
                        handleJoinRoom(session, payload);
                        System.out.println(" joinRoom");
                        break;
                    case "playerAction":
                        handlePlayerAction(session, payload);
                        break;
                    case "playerDeath":
                        handlePlayerDeath(session, payload);
                        System.out.println(" playerDeath");
                        break;
                    case "heartbeat":
                        session.sendMessage(new TextMessage("{\"type\":\"heartbeat\",\"status\":\"ok\"}"));
                        break;
                }
            }
        } catch (Exception e) {
            System.err.println("@#@#@#@#处理消息错误: " + e.getMessage());
            safeSendMessage(session, "{\"error\":\"Invalid message format\"}");
        }
    }

    private void handleJoinRoom(WebSocketSession session, Map<String, Object> payload) throws IOException {
        int maxPlayers = (Integer) payload.get("maxPlayers");
        String roomId = findAvailableRoom(maxPlayers);

        if (roomId == null) {
            roomId = String.valueOf(System.currentTimeMillis());
            GameRoom newRoom = new GameRoom();
            newRoom.setId(roomId);
            newRoom.setMaxPlayers(maxPlayers);
            rooms.put(roomId, newRoom);
        }

        GameRoom room = rooms.get(roomId);
        Player player = new Player();
        player.setId(session.getId());
        player.setDirection(new Direction(1, 0)); // Default direction right
        room.getPlayers().add(player);

        Map<String, Object> response = new HashMap<>();
        response.put("type", "roomId");
        response.put("roomId", roomId);
        session.sendMessage(new TextMessage(mapper.writeValueAsString(response)));

        updatePlayerList(roomId);
    }

    private String findAvailableRoom(int maxPlayers) {
        for (GameRoom room : rooms.values()) {
            if (room.getPlayers().size() < maxPlayers) {
                return room.getId();
            }
        }
        return null;
    }

    private void handlePlayerAction(WebSocketSession session, Map<String, Object> payload) throws IOException {
        String roomId = findRoomByPlayer(session.getId());
        if (roomId != null) {
            GameRoom room = rooms.get(roomId);
            // 根据当前 WebSocket 会话，在游戏房间中查找对应的玩家对象，如果找不到则返回 null。
            Player player = room.getPlayers().stream()
                    .filter(p -> p.getId().equals(session.getId()))
                    .findFirst()
                    .orElse(null);

            if (player != null) {
                // 获取方向前先验证类型
                Object directionObj = payload.get("direction");
                if (!(directionObj instanceof Map)) {
                    // 可以记录错误日志或发送错误消息给客户端
                    System.err.println("Invalid direction format received");
                    return;
                }
                // Update player data from payload
                // 现在可以安全转换
                @SuppressWarnings("unchecked")
                Map<String, Object> directionMap = (Map<String, Object>) payload.get("direction");

                player.setDirection(new Direction(
                        ((Number) directionMap.get("x")).doubleValue(),
                        ((Number) directionMap.get("y")).doubleValue()
                ));
                System.out.println("Player " + player + " moved.");

                // Similarly update snake and score...

                broadcastGameUpdate(room);
            }
        }
    }

    private void handlePlayerDeath(WebSocketSession session, Map<String, Object> payload) throws IOException {
        String playerId = (String) payload.get("playerId");
        String roomId = findRoomByPlayer(playerId);
        System.out.println(session + "，@@@@@@@" );

        if (roomId != null) {
            System.out.println("Player " + playerId + " died.");
            GameRoom room = rooms.get(roomId);
            room.setPlayers(room.getPlayers().stream()
                    .filter(p -> !p.getId().equals(playerId))
                    .collect(Collectors.toList()));

            updatePlayerList(roomId);
            broadcastGameUpdate(room);
        }
    }

    private void updatePlayerList(String roomId) throws IOException {
        GameRoom room = rooms.get(roomId);
        if (room != null) {
            Map<String, Object> message = new HashMap<>();
            message.put("type", "playerList");
            message.put("players", room.getPlayers());

            for (Player player : room.getPlayers()) {
                WebSocketSession playerSession = sessions.get(player.getId());
                if (playerSession != null && playerSession.isOpen()) {
                    safeSendMessage(playerSession, mapper.writeValueAsString(message));
                }
            }
        }
    }

    private void broadcastGameUpdate(GameRoom room) throws IOException {
        Map<String, Object> gameState = new HashMap<>();
        gameState.put("type", "gameUpdate");
        gameState.put("players", room.getPlayers().stream().map(
                player -> {
                    Map<String, Object> playerData = new HashMap<>();
                    playerData.put("id", player.getId());
                    playerData.put("snake", player.getSnake()); // 假设Player类有getSnake方法
                    playerData.put("score", player.getScore());
                    return playerData;
                }).collect(Collectors.toList()));
//        gameState.put("foods", room.getFoods());
        gameState.put("powerUps", room.getPowerUps());

        String message = mapper.writeValueAsString(gameState);

        for (Player player : room.getPlayers()) {
            WebSocketSession session = sessions.get(player.getId());
            if (session != null && session.isOpen()) {
                safeSendMessage( session, message);
            }
        }
    }

    // 安全发送消息方法
    private void safeSendMessage(WebSocketSession session, String message) {
        if (!session.isOpen()) {
            return;
        }

        messageExecutor.submit(() -> {
            Object sessionLock = sessionLocks.computeIfAbsent(session.getId(), k -> new Object());
            synchronized (sessionLock) {
                try {
                    if (session.isOpen()) {
                        session.sendMessage(new TextMessage(message));
                    }
                } catch (IllegalStateException e) {
                    // 处理TEXT_PARTIAL_WRITING状态
                    if (e.getMessage().contains("TEXT_PARTIAL_WRITING")) {
                        System.err.println("消息发送冲突，将重试: " + e.getMessage());
                        try {
                            Thread.sleep(50); // 短暂等待后重试
                            if (session.isOpen()) {
                                session.sendMessage(new TextMessage(message));
                            }
                        } catch (Exception ex) {
                            System.err.println("重试发送失败: " + ex.getMessage());
                        }
                    }
                } catch (IOException e) {
                    System.err.println("发送消息失败: " + e.getMessage());
                }
            }
        });
    }


    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        // 移除session锁
        sessionLocks.remove(session.getId());
        // 移除session
        sessions.remove(session.getId());
        String roomId = findRoomByPlayer(session.getId());

        if (roomId != null) {
            GameRoom room = rooms.get(roomId);
            room.setPlayers(room.getPlayers().stream()
                    .filter(p -> !p.getId().equals(session.getId()))
                    .collect(Collectors.toList()));

            if (room.getPlayers().isEmpty()) {
                rooms.remove(roomId);
            } else {
                try {
                    updatePlayerList(roomId);
                    broadcastGameUpdate(room);
                } catch (IOException e) {
                    System.out.println("广播游戏更新失败: " + e.getMessage());
                }
            }
        }
    }

    private String findRoomByPlayer(String playerId) {
        for (GameRoom room : rooms.values()) {
            if (room.getPlayers().stream().anyMatch(p -> p.getId().equals(playerId))) {
                return room.getId();
            }
        }
        return null;
    }
}
